<!--
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
-->
<link rel="import" href="../../components/polymer/polymer.html">
<link rel="import" href="../../components/iron-icon/iron-icon.html">
<link rel="import" href="line-chart.html">
<dom-module id="testrtc-test">
  <style>
    .test-output {
      margin: 0 2em 0 2em;
      word-wrap: break-word;
    }

    paper-progress {
      display: block;
      min-width: 2em;
      flex: 0.000001;
    }

    .header {
      background-color: transparent;
      height: 2em;
      min-width: 80%;
      display: flex;
      padding-left: 1em;
      padding-right: 1em;
      align-items: center;
      cursor: pointer;
      font-size: 1.3em;
    }

    iron-collapse {
      padding-bottom: 0.5em;
    }

    .header .title {
      flex: 1;
      font-weight: 300;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    :host[state='success'] iron-icon {
      color: #080;
    }

    :host[state='failure'] iron-icon {
      color: #F00;
    }
  </style>
  <template>
    <div class="header" on-tap="toggle">
      <span class="title">{{name}}</span>
      <paper-progress hidden$="[[!_inProgress(progressValue)]]" value="[[progressValue]]"></paper-progress>
      <iron-icon hidden$="[[_inProgress(progressValue)]]" icon="[[_iconForState(state)]]"></iron-icon>
    </div>
    <iron-collapse id="collapse">
      <div id="plot" class="test-output"></div>
      <template is="dom-repeat" items="[[output]]" as="item">
        <div class="test-output"><span>[[item.prefix]]</span> <span>[[item.message]]</span></div>
      </template>
    </iron-collapse>
  </template>
  <script>
    var PREFIX_INFO    = '[   INFO ]';
    var PREFIX_OK      = '[     OK ]';
    var PREFIX_FAILED  = '[ FAILED ]';
    var PREFIX_WARNING = '[   WARN ]';

    Polymer({
      is: 'testrtc-test',
      properties: {
        name: { type: String },
        testFunction: { type: Function },
        settings: {
          type: Object,
          value: function() { return {}; }
        },
        state: {
          value: "unknown",
          reflectToAttribute: true
        },
        progressValue: { value: null },
        output: { type: Array, value: function() { return []; } }
      },

      ready: function() {
        this.reportMessage_(PREFIX_INFO, 'Test not run yet.');
      },

      _inProgress: function(value) {
        return value !== null;
      },

      _iconForState: function(state) {
        if (state === 'running') return 'more-horiz';
        if (state === 'success') return 'check';
        if (state === 'warning') return 'warning';
        if (state === 'failure') return 'close';
        if (state === 'disabled') return '';
        return '';
      },

      toggle: function() {
        this.$.collapse.toggle();
      },

      run: function(doneCallback) {
        // In order to avoid settings being updated while a test is run the value
        // is stored into _settings before starting the test run.
        // TODO(andresp): Propagate settings properly.
        this.settings = document.getElementsByTagName('testrtc-main')[0].settings;
        this.successCount = 0;
        this.warningCount = 0;
        this.errorCount = 0;
        this.doneCallback_ = doneCallback;
        this.output = [];
        this.clearPlots();

        this.setProgress(null);
        this.traceTestEvent = report.traceEventAsync('test-run');

        currentTest = this;
        this.state = "running";
        this.traceTestEvent({name: this.name, status: this.state});
        if (!this.isDisabled) {
          // Pass in the "this" object for test reporting and control.
          this.testFunction(this);
        } else {
          this.reportInfo('Test is disabled.');
          this.done();
        }
      },

      done: function() {
        if (this.state !== "running") {
          return;
        }
        this.setProgress(null);
        var success =
          (this.errorCount + this.warningCount === 0 && this.successCount > 0);

        if (success) {
          this.state = 'success';
        } else if (this.warningCount > 0 && this.errorCount === 0) {
          this.state = 'warning';
        } else if (this.isDisabled) {
          this.state = 'disabled';
        } else {
          this.state = 'failure';
        }

        this.traceTestEvent({status: this.state});
        report.logTestRunResult(this.name, this.state);
        this.doneCallback_();
      },

      setProgress: function(value) {
        this.progressValue = value;
      },

      expectEquals: function(expected, actual, failMsg, okMsg) {
        if (expected !== actual) {
          this.reportError('Failed expectation: ' + expected + ' !== ' + actual +
                           ': ' + failMsg);
        } else if (okMsg) {
          this.reportSuccess(okMsg);
        }
      },

      reportSuccess: function(str) {
        this.reportMessage_(PREFIX_OK, str);
        this.successCount++;
        this.traceTestEvent({success: str});
      },

      reportError: function(str) {
        this.reportMessage_(PREFIX_FAILED, str);
        this.errorCount++;
        this.traceTestEvent({error: str});
      },

      reportWarning: function(str) {
        this.reportMessage_(PREFIX_WARNING, str);
        this.warningCount++;
        this.traceTestEvent({warning: str});
      },

      reportInfo: function(str) {
        this.reportMessage_(PREFIX_INFO, str);
        this.traceTestEvent({info: str});
      },

      // Use only for error callbacks upon test invocation, not to end the test,
      // use report<Status>(message) and setTestFinished() for that.
      // TODO: Figure out a better way to do this.
      reportFatal: function(str) {
        this.reportError(str);
        this.done();
      },

      reportMessage_: function(prefix, str) {
        // TODO: silly unnecessary reassign.
        this.output = [].concat(this.output, [{prefix: prefix, message: str}]);
      },

      clearPlots: function() {
        while (this.$.plot.lastElementChild !== null) {
          Polymer.dom(this.$.plot).removeChild(this.$.plot.lastElementChild);
        }
      },
      createLineChart: function() {
        var chart = document.createElement('line-chart');
        Polymer.dom(this.$.plot).appendChild(chart);
        this.$.collapse.opened = true;
        return chart;
      }
    });
  </script>
</dom-module>

<script>
// TODO(andresp): Pass Test object to test instead of using global methods.
var currentTest;
function createLineChart() { return currentTest.createLineChart(); }
function reportSuccess(str) { currentTest.reportSuccess(str); }
function reportError(str) { currentTest.reportError(str); }
function reportFatal(str) { currentTest.reportFatal(str); }
function reportWarning(str) { currentTest.reportWarning(str); }
function reportInfo(str) { currentTest.reportInfo(str); }
function setTestProgress(value) { currentTest.setProgress(value); }
function setTestFinished() { currentTest.done(); }
function expectEquals() { currentTest.expectEquals.apply(currentTest,
                                                         arguments); }

// TODO(jansson): Remove from global scope, add it to test object?
function setTimeoutWithProgressBar(timeoutCallback, timeoutMs) {
  var start = window.performance.now();
  var updateProgressBar = setInterval(function() {
    var now = window.performance.now();
    setTestProgress((now - start) * 100 / timeoutMs);
  }, 100);

  var timeoutTask = function() {
    clearInterval(updateProgressBar);
    setTestProgress(100);
    timeoutCallback();
  };
  var timer = setTimeout(timeoutTask, timeoutMs);
  var finishProgressBar = function() {
    clearTimeout(timer);
    timeoutTask();
  };
  return finishProgressBar;
}
</script>
